package masterjava.common.client;

import com.sun.xml.ws.client.BindingProviderProperties;
import masterjava.common.property.Property;
import masterjava.common.ws.SoapClientLoggingHandler;

import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.soap.SOAPFaultException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * MasterJava.ru
 * <p/>
 * User: GKislin
 * Date: 28.03.11
 */
public class WsClient<ITF> extends AbstractClient<ITF, URL> {

    public WsClient(String serviceName, Property<URL> wsdlLocation) {
        super(serviceName, wsdlLocation);
    }

    @Override
    protected boolean isClientUnavailable(Throwable e) {
        return (e instanceof WebServiceException);
    }

    @Override
    protected boolean isClientReinitialize(Throwable e) {
        return (e instanceof SOAPFaultException);
    }

    @Override
    protected ITF doInit(){
        return initWebService();
    }

    public ITF initWebService() {
        String serviceName = serviceClass.getAnnotation(WebService.class).name();
        String targetNamspace = serviceClass.getAnnotation(WebService.class).targetNamespace();
        return initWebService(targetNamspace, null, serviceName);
    }

    public ITF initWebService(String targetNamspace, String servicename, String portName) {
        if (serviceLocation.get() == null) {
            return null;
        }
        Service service = Service.create(serviceLocation.get(), new QName(targetNamspace, servicename));
        ITF port = (portName == null) ? service.getPort(serviceClass) : service.getPort(new QName(targetNamspace, portName), serviceClass);

        BindingProvider prov = ((BindingProvider) port);
        Map<String, Object> ctx = prov.getRequestContext();
        ctx.put(BindingProviderProperties.CONNECT_TIMEOUT, getTimeout());
        ctx.put(BindingProviderProperties.REQUEST_TIMEOUT, getTimeout());

        Binding binding = prov.getBinding();
        List<Handler> handlerList = binding.getHandlerChain();
        if (handlerList == null) {
            handlerList = Collections.singletonList(SoapClientLoggingHandler.CLIENT_LOGGING);
        } else {
            handlerList.add(SoapClientLoggingHandler.CLIENT_LOGGING);
        }
        binding.setHandlerChain(handlerList);
        return port;
    }

    private int getTimeout() {
        return 30000;  // 30 sec default
    }
}
